{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oA823Xm-LG0c"
   },
   "source": [
    "# Кэш в GigaChain\n",
    "\n",
    "Здесь мы рассмотрим:\n",
    "1. Как в GigaChain framework используется кэширование для оптимизации запросов к LLM модели\n",
    "2. Два разных способа кэширования запросов, In-Memory и SQLite.\n",
    "\n",
    "Надеемся это поможет вам разобраться зачем и когда стоит использовать кэширование"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "VXeDv_iNy4Yt"
   },
   "outputs": [],
   "source": [
    "!pip install gigachain --quiet --upgrade"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GFr7TU4yMINW"
   },
   "source": [
    "## Подготавливаем к работе инстанс GigaChat\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "9dBrwRdizKI4"
   },
   "outputs": [],
   "source": [
    "import langchain\n",
    "from langchain.llms import GigaChat\n",
    "\n",
    "llm = GigaChat(credentials=\"<Ваши авторизационные данные>\", verify_ssl_certs=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "U9LI6MsOMYDz"
   },
   "source": [
    "## 1. In Memory Cache"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "pZkMY2T1zRJ4"
   },
   "outputs": [],
   "source": [
    "from langchain.cache import InMemoryCache\n",
    "\n",
    "langchain.llm_cache = InMemoryCache()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "A_0EEZ6BMgHf"
   },
   "source": [
    "### Спросим GigaChat вопрос и измерим сколько потребуется LLM модели времени, чтобы ответить на него."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 89
    },
    "id": "EOxV7tZwz0Jz",
    "outputId": "a8e2179a-d819-48b0-9ed3-102b609a3776"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 44.9 ms, sys: 5.27 ms, total: 50.2 ms\n",
      "Wall time: 2.83 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Я — модель искусственного интеллекта, созданный для помощи людям. Моя задача — отвечать на вопросы и предоставлять информацию по различным темам. Я могу помочь вам с переводом или объяснением сложных концепций, а также предоставить факты и статистику. Если у вас есть какие-либо конкретные запросы или вопросы, пожалуйста, задайте их мне, и я постараюсь помочь вам наилучшим образом.'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm.predict(\"Кто ты?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wNVz-A70OB65"
   },
   "source": [
    "#### Как InMemoryCache кэш хранит данные\n",
    "\n",
    "**source code**: [cache.py](https://github.com/hwchase17/langchain/blob/v0.0.219/langchain/cache.py#L102)\n",
    "```python\n",
    "class InMemoryCache(BaseCache):\n",
    "    \"\"\"Cache that stores things in memory.\"\"\"\n",
    "\n",
    "    def __init__(self) -> None:\n",
    "        \"\"\"Initialize with empty cache.\"\"\"\n",
    "        self._cache: Dict[Tuple[str, str], RETURN_VAL_TYPE] = {}\n",
    "```\n",
    "\n",
    "Это реализация InMemoryCache.\n",
    "Она хранит данные в своей оперативной памяти"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 35
    },
    "id": "795SesMkKsx1",
    "outputId": "8105246f-6307-4e8e-a289-6e8853cd8158"
   },
   "outputs": [
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'[{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"\\\\u041a\\\\u0442\\\\u043e \\\\u0442\\\\u044b?\"}}]'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Первый элемент tuple в кэше\n",
    "list(langchain.llm_cache._cache.keys())[0][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 53
    },
    "id": "cKoCMgV1J0Qy",
    "outputId": "832ae737-1c0f-49a4-facc-57ba107bce99"
   },
   "outputs": [
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"chat_models\", \"gigachat\", \"GigaChat\"], \"kwargs\": {\"oauth_token\": {\"lc\": 1, \"type\": \"secret\", \"id\": [\"GIGA_OAUTH_TOKEN\"]}, \"verify_ssl\": false, \"oauth_verify_ssl\": false}}---[(\\'stop\\', None)]'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Второй элемент tuple в кэше\n",
    "list(langchain.llm_cache._cache.keys())[0][1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "s0HhgOmWMomV"
   },
   "source": [
    "### Зададим тот же вопрос и получим ответ быстрее"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 89
    },
    "id": "GDykW5yiMw-b",
    "outputId": "e52f26c9-171f-4c8b-95ba-bdaae804e44a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.86 ms, sys: 0 ns, total: 1.86 ms\n",
      "Wall time: 1.88 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Я — модель искусственного интеллекта, созданный для помощи людям. Моя задача — отвечать на вопросы и предоставлять информацию по различным темам. Я могу помочь вам с переводом или объяснением сложных концепций, а также предоставить факты и статистику. Если у вас есть какие-либо конкретные запросы или вопросы, пожалуйста, задайте их мне, и я постараюсь помочь вам наилучшим образом.'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm.predict(\"Кто ты?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vYonfxGfMx3t"
   },
   "source": [
    "## 2. SQLite в качестве кэша"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "EP3QRaPy0mp1"
   },
   "outputs": [],
   "source": [
    "!rm -f .cache.db"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "yRFlThqU0tfU"
   },
   "outputs": [],
   "source": [
    "from langchain.cache import SQLiteCache\n",
    "\n",
    "langchain.llm_cache = SQLiteCache(database_path=\".cache.db\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2j1dHYmGM5WK"
   },
   "source": [
    "### Зададим один и тот же вопрос дважды и измерим разницу в производительности"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 89
    },
    "id": "WsfcYsU40yFR",
    "outputId": "bf7af30e-3101-4d5e-a0d2-623ff807f8f5"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 31.6 ms, sys: 3.01 ms, total: 34.6 ms\n",
      "Wall time: 1.97 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Я — модель искусственного интеллекта, созданный для помощи людям. Моя задача — отвечать на вопросы и предоставлять информацию по различным темам. Я могу создавать тексты, изображения и генерировать идеи. Если у вас есть какие-либо вопросы или нужна помощь, не стесняйтесь обращаться ко мне!'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm.predict(\"Кто ты?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 89
    },
    "id": "m_HFoa-Z052V",
    "outputId": "39a9a1f5-69d9-4244-c20b-985a69c133db"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 2.8 ms, sys: 884 µs, total: 3.69 ms\n",
      "Wall time: 3.44 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Я — модель искусственного интеллекта, созданный для помощи людям. Моя задача — отвечать на вопросы и предоставлять информацию по различным темам. Я могу создавать тексты, изображения и генерировать идеи. Если у вас есть какие-либо вопросы или нужна помощь, не стесняйтесь обращаться ко мне!'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm.predict(\"Кто ты?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "hHIjReJUM_gD"
   },
   "source": [
    "### Добавим лишний пробел в вопросе и посмотрим что будет"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "hdD1CpzSNFzo",
    "outputId": "039ad57d-40a3-4401-d38c-ab87c8b0a3f2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 25.3 ms, sys: 1.75 ms, total: 27 ms\n",
      "Wall time: 1.75 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Я — мультимодальная нейросетевая модель, созданная командой разработчиков из Сбера. Я умею отвечать на вопросы пользователей, поддерживать диалог, создавать тексты разных форматов и стилей, а также генерировать картинки по запросу.'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm.predict(\"Кто  ты?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "TvI7KFBLGfTn"
   },
   "outputs": [],
   "source": [
    "import sqlalchemy\n",
    "from sqlalchemy import create_engine, text\n",
    "\n",
    "engine = create_engine(\"sqlite:///.cache.db\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Y4LAKj76NJiZ"
   },
   "source": [
    "### **Почему лишний пробел не дает нормально срабатывать кэшу??**\n",
    "Потому что кэш ищет точное совпадние запроса. Мы делали запрос без лишнего пробела и он сохранился без него, соответсвенно ответ ни в InMemory, ни в SQLite не будет найден"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "hlKSAcOOOSqA"
   },
   "source": [
    "#### Как SQLite хранит данные\n",
    "\n",
    "**source code**: [cache.py](https://github.com/hwchase17/langchain/blob/v0.0.219/langchain/cache.py#L128)\n",
    "```python\n",
    "class FullLLMCache(Base):  # type: ignore\n",
    "    \"\"\"SQLite table for full LLM Cache (all generations).\"\"\"\n",
    "\n",
    "    __tablename__ = \"full_llm_cache\"\n",
    "    prompt = Column(String, primary_key=True)\n",
    "    llm = Column(String, primary_key=True)\n",
    "    idx = Column(Integer, primary_key=True)\n",
    "    response = Column(String)\n",
    "\n",
    "\n",
    "class SQLAlchemyCache(BaseCache):\n",
    "    \"\"\"Cache that uses SQAlchemy as a backend.\"\"\"\n",
    "\n",
    "    def __init__(self, engine: Engine, cache_schema: Type[FullLLMCache] = FullLLMCache):\n",
    "        \"\"\"Initialize by creating all tables.\"\"\"\n",
    "        self.engine = engine\n",
    "        self.cache_schema = cache_schema\n",
    "        self.cache_schema.metadata.create_all(self.engine)\n",
    "```\n",
    "\n",
    "Это схема SQL таблицы `full_llm_cache`, которая хранит данные кэша."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "RK8cQdbkGrNk",
    "outputId": "bb126917-9fc4-4bf0-8991-42254798c8fd"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RMKeyView(['prompt', 'llm', 'idx', 'response'])\n",
      "('[{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"\\\\u041a\\\\u0442\\\\u043e \\\\u0442\\\\u044b?\"}}]', '{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"chat_models\", \"gigachat\", \"GigaChat\"], \"kwargs\": {\"oauth_token\": {\"lc\": 1, \"type\": \"secret\", \"id\": [\"GIGA_OAUTH_TOKEN\"]}, \"verify_ssl\": false, \"oauth_verify_ssl\": false}}---[(\\'stop\\', None)]', 0, '{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"output\", \"ChatGeneration\"], \"kwargs\": {\"message\": {\"lc\": 1, \"type\": \"constructor\", \"i ... (1709 characters truncated) ... 0431\\\\u0440\\\\u0430\\\\u0449\\\\u0430\\\\u0442\\\\u044c\\\\u0441\\\\u044f \\\\u043a\\\\u043e \\\\u043c\\\\u043d\\\\u0435!\"}}, \"generation_info\": {\"finish_reason\": \"stop\"}}}')\n",
      "('[{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"\\\\u041a\\\\u0442\\\\u043e  \\\\u0442\\\\u044b?\"}}]', '{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"chat_models\", \"gigachat\", \"GigaChat\"], \"kwargs\": {\"oauth_token\": {\"lc\": 1, \"type\": \"secret\", \"id\": [\"GIGA_OAUTH_TOKEN\"]}, \"verify_ssl\": false, \"oauth_verify_ssl\": false}}---[(\\'stop\\', None)]', 0, '{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"output\", \"ChatGeneration\"], \"kwargs\": {\"message\": {\"lc\": 1, \"type\": \"constructor\", \"i ... (1386 characters truncated) ... 0442\\\\u0438\\\\u043d\\\\u043a\\\\u0438 \\\\u043f\\\\u043e \\\\u0437\\\\u0430\\\\u043f\\\\u0440\\\\u043e\\\\u0441\\\\u0443.\"}}, \"generation_info\": {\"finish_reason\": \"stop\"}}}')\n"
     ]
    }
   ],
   "source": [
    "with engine.connect() as connection:\n",
    "    rs = connection.exec_driver_sql(\"select * from full_llm_cache\")\n",
    "    print(rs.keys())\n",
    "    for row in rs:\n",
    "        print(row)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "a46Ty0dmfhRH"
   },
   "source": [
    "## Семантический кэш\n",
    "\n",
    "Семантический кэш хранит промпты и ответы, и получает запросы из кэша исходя из схожести запросов по смыслу.\n",
    "Для того, чтобы работать с семантическим кэшем на данный момент нужно использовать OpenAI или другие ембединги, так как на текущий момент у GigaChat'а ембедингов нет."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "O5tV0sRSqIUT"
   },
   "outputs": [],
   "source": [
    "!pip install gigachain openai tiktoken --quiet --upgrade"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "EwC6ItJngOoo"
   },
   "source": [
    "### Следуя доке [Redis](https://redis.com/blog/running-redis-on-google-colab/) устанавливаем и запускаем RedisServer на Google Colab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "F-kFFz5oaPHv"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"<Ваш OpenAI токен>\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "AHH23I9ngMGy",
    "outputId": "f988010c-abef-40a8-908f-1381c0335657"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./\n",
      "./redis-stack-server-6.2.6-v7/\n",
      "./redis-stack-server-6.2.6-v7/bin/\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-benchmark\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-cli\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-sentinel\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-stack-server\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-check-rdb\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-check-aof\n",
      "./redis-stack-server-6.2.6-v7/bin/redis-server\n",
      "./redis-stack-server-6.2.6-v7/share/\n",
      "./redis-stack-server-6.2.6-v7/share/RSAL_LICENSE\n",
      "./redis-stack-server-6.2.6-v7/share/APACHE_LICENSE\n",
      "./redis-stack-server-6.2.6-v7/lib/\n",
      "./redis-stack-server-6.2.6-v7/lib/redisgraph.so\n",
      "./redis-stack-server-6.2.6-v7/lib/redistimeseries.so\n",
      "./redis-stack-server-6.2.6-v7/lib/rejson.so\n",
      "./redis-stack-server-6.2.6-v7/lib/redisbloom.so\n",
      "./redis-stack-server-6.2.6-v7/lib/redisearch.so\n",
      "./redis-stack-server-6.2.6-v7/etc/\n",
      "./redis-stack-server-6.2.6-v7/etc/README\n",
      "./redis-stack-server-6.2.6-v7/etc/redis-stack.conf\n",
      "./redis-stack-server-6.2.6-v7/etc/redis-stack-service.conf\n",
      "Collecting redis\n",
      "  Downloading redis-5.0.1-py3-none-any.whl (250 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m250.3/250.3 kB\u001b[0m \u001b[31m3.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: async-timeout>=4.0.2 in /usr/local/lib/python3.10/dist-packages (from redis) (4.0.3)\n",
      "Installing collected packages: redis\n",
      "Successfully installed redis-5.0.1\n",
      "--2023-10-03 12:04:21--  http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb\n",
      "Resolving nz2.archive.ubuntu.com (nz2.archive.ubuntu.com)... 91.189.91.83, 185.125.190.36, 91.189.91.82, ...\n",
      "Connecting to nz2.archive.ubuntu.com (nz2.archive.ubuntu.com)|91.189.91.83|:80... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 1321244 (1.3M) [application/vnd.debian.binary-package]\n",
      "Saving to: ‘libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb’\n",
      "\n",
      "libssl1.1_1.1.1f-1u 100%[===================>]   1.26M  2.16MB/s    in 0.6s    \n",
      "\n",
      "2023-10-03 12:04:29 (2.16 MB/s) - ‘libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb’ saved [1321244/1321244]\n",
      "\n",
      "Selecting previously unselected package libssl1.1:amd64.\n",
      "(Reading database ... 120895 files and directories currently installed.)\n",
      "Preparing to unpack libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb ...\n",
      "Unpacking libssl1.1:amd64 (1.1.1f-1ubuntu2.19) ...\n",
      "Setting up libssl1.1:amd64 (1.1.1f-1ubuntu2.19) ...\n",
      "debconf: unable to initialize frontend: Dialog\n",
      "debconf: (No usable dialog-like program is installed, so the dialog based frontend cannot be used. at /usr/share/perl5/Debconf/FrontEnd/Dialog.pm line 78.)\n",
      "debconf: falling back to frontend: Readline\n",
      "debconf: unable to initialize frontend: Readline\n",
      "debconf: (This frontend requires a controlling tty.)\n",
      "debconf: falling back to frontend: Teletype\n",
      "Processing triggers for libc-bin (2.35-0ubuntu3.1) ...\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbbbind.so.3 is not a symbolic link\n",
      "\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_0.so.3 is not a symbolic link\n",
      "\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_5.so.3 is not a symbolic link\n",
      "\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc.so.2 is not a symbolic link\n",
      "\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbb.so.12 is not a symbolic link\n",
      "\n",
      "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc_proxy.so.2 is not a symbolic link\n",
      "\n",
      "Starting redis-stack-server, database path ./redis-stack-server-6.2.6-v7/var/db/redis-stack\n"
     ]
    }
   ],
   "source": [
    "!curl -fsSL https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v7.focal.x86_64.tar.gz -o redis-stack-server.tar.gz\n",
    "!tar -xvf redis-stack-server.tar.gz\n",
    "!pip install redis\n",
    "!wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb\n",
    "!sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb\n",
    "\n",
    "!./redis-stack-server-6.2.6-v7/bin/redis-stack-server --daemonize yes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "flJ_q0ymfyEb"
   },
   "outputs": [],
   "source": [
    "import langchain\n",
    "from langchain.llms import GigaChat\n",
    "\n",
    "llm = GigaChat(credentials=\"<Ваши авторизационные данные>\", verify_ssl_certs=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "m3GDztDVpUGQ"
   },
   "source": [
    "### Инициализируем семантический Redis кэш с дефолтным score threshold 0.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "YylPXZ2dgiHc"
   },
   "outputs": [],
   "source": [
    "from langchain.cache import RedisSemanticCache\n",
    "from langchain.embeddings import OpenAIEmbeddings\n",
    "\n",
    "langchain.llm_cache = RedisSemanticCache(\n",
    "    redis_url=\"redis://localhost:6379\",\n",
    "    embedding=OpenAIEmbeddings(),\n",
    "    score_threshold=0.1,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "r3c0garCgp-9",
    "outputId": "ab8aa600-6b6a-4468-daac-1a0527db34e9"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 146 ms, sys: 5.61 ms, total: 151 ms\n",
      "Wall time: 4.12 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'上午 (Sìpíng)'"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Переведи 'сегодня понедельник' на китайский\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dIu-A7Wxn0sT"
   },
   "source": [
    "Заметьте что, запрос отличается на 1 слово от предыдущего. Кэш вернул старый ответ, так как score threshold 0.2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "bm_QBd4gnw_w",
    "outputId": "6e138e83-5a78-4dbf-e683-116978ba198e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 10.4 ms, sys: 0 ns, total: 10.4 ms\n",
      "Wall time: 696 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'上午 (Sìpíng)'"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Переведи 'сегодня вторник' на китайский\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "KB27Wo5ihC-C",
    "outputId": "70cf60cb-13dc-4002-e485-e61996f482ae"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 40.8 ms, sys: 6.05 ms, total: 46.9 ms\n",
      "Wall time: 3.52 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Нашла для вас такую шутку: \\n\\n— Почему блондинки красят корни волос в чёрный цвет? \\n— Потому что они не хотят быть как брюнетки!'"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Расскажи мне шутку\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "Xbp5si6tpb7E",
    "outputId": "a65dbe83-0d03-4592-fd93-08b264abf077"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 8.29 ms, sys: 137 µs, total: 8.43 ms\n",
      "Wall time: 677 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Нашла для вас такую шутку: \\n\\n— Почему блондинки красят корни волос в чёрный цвет? \\n— Потому что они не хотят быть как брюнетки!'"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Расскажи мне 2 шутки\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lvggtYaDpiV1"
   },
   "source": [
    "### Инициализируем семантический Redis кэш с дефолтным score threshold 0.005"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "zIjlyavcpk9F"
   },
   "outputs": [],
   "source": [
    "import redis\n",
    "\n",
    "client = redis.Redis(host=\"localhost\", port=6379)\n",
    "client.flushall()  # очищаем старый кэш\n",
    "langchain.llm_cache = RedisSemanticCache(\n",
    "    redis_url=\"redis://localhost:6379\",\n",
    "    embedding=OpenAIEmbeddings(),\n",
    "    score_threshold=0.05,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "ybsRNjYhhIs5",
    "outputId": "1c20413f-783e-4204-eaf0-fefe4a754776"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 42.8 ms, sys: 8.82 ms, total: 51.6 ms\n",
      "Wall time: 3.58 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Нашла для вас такую шутку: \\n\\n— А я вчера слона купил! \\n— Ну и как? \\n— Маленький такой слонёнок, миленький. Назвал его Гошей. Только вот не знаю, что теперь с ним делать…'"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Расскажи мне шутку\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "M9D4a_pHfLRI"
   },
   "source": [
    "*Запрос с лишним пробелом возмется из кэша*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "zfq2rErdk5zZ",
    "outputId": "c7c83836-6f20-45e1-ce71-273721b0d28c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 9.23 ms, sys: 0 ns, total: 9.23 ms\n",
      "Wall time: 588 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Нашла для вас такую шутку: \\n\\n— А я вчера слона купил! \\n— Ну и как? \\n— Маленький такой слонёнок, миленький. Назвал его Гошей. Только вот не знаю, что теперь с ним делать…'"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Расскажи мне  шутку\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1YAFYoU3fSVe"
   },
   "source": [
    "*А вот запрос двух шуток уже пойдет в гигачат*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "RTT86hLHfGlo",
    "outputId": "f8385b27-9832-4189-a0ac-27093189e715"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 39 ms, sys: 90 µs, total: 39.1 ms\n",
      "Wall time: 2.55 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Конечно! Вот две хорошие шутки:\\n\\n1. Почему слоны не играют в карты? Потому что они всегда выигрывают.\\n\\n2. Как называется человек, который всё забывает? Склеротик. А как называется тот, кто всё помнит? Злопамятный.'"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Расскажи мне 2 шутки\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "p25RH2EioFmv"
   },
   "source": [
    "### Глубокое погружение в Redis семантический кэш"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nimL_nl7oK1s"
   },
   "source": [
    "#### Ищем запросы в кэше"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "l4eGNhNIng9K",
    "outputId": "013baf6f-41c3-4a8e-a96a-17811f3e0daa"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'cache:54d4d5d60796b5c24e291e4179785fe1': <langchain.vectorstores.redis.base.Redis at 0x7a42d48dae00>}"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "langchain.llm_cache._cache_dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "y-GsORdkoQOG"
   },
   "source": [
    "#### Вручную выполняем поиск запросов\n",
    "\n",
    "Чем больше запрос похож на другой тем ближе он будет в массиве"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ZqSiGnAsmC2p",
    "outputId": "c0e8b541-f1e2-44bb-8a77-6be55e1b3dfa"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='Расскажи мне шутку', metadata={'id': 'doc:cache:54d4d5d60796b5c24e291e4179785fe1:587093b63ad44a2daa6c614ccd23dabe', 'return_val': '[{\"text\": \"\\\\u041d\\\\u0430\\\\u0448\\\\u043b\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0432\\\\u0430\\\\u0441 \\\\u0442\\\\u0430\\\\u043a\\\\u0443\\\\u044e \\\\u0448\\\\u0443\\\\u0442\\\\u043a\\\\u0443: \\\\n\\\\n\\\\u2014 \\\\u0410 \\\\u044f \\\\u0432\\\\u0447\\\\u0435\\\\u0440\\\\u0430 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u0430 \\\\u043a\\\\u0443\\\\u043f\\\\u0438\\\\u043b! \\\\n\\\\u2014 \\\\u041d\\\\u0443 \\\\u0438 \\\\u043a\\\\u0430\\\\u043a? \\\\n\\\\u2014 \\\\u041c\\\\u0430\\\\u043b\\\\u0435\\\\u043d\\\\u044c\\\\u043a\\\\u0438\\\\u0439 \\\\u0442\\\\u0430\\\\u043a\\\\u043e\\\\u0439 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u0451\\\\u043d\\\\u043e\\\\u043a, \\\\u043c\\\\u0438\\\\u043b\\\\u0435\\\\u043d\\\\u044c\\\\u043a\\\\u0438\\\\u0439. \\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430\\\\u043b \\\\u0435\\\\u0433\\\\u043e \\\\u0413\\\\u043e\\\\u0448\\\\u0435\\\\u0439. \\\\u0422\\\\u043e\\\\u043b\\\\u044c\\\\u043a\\\\u043e \\\\u0432\\\\u043e\\\\u0442 \\\\u043d\\\\u0435 \\\\u0437\\\\u043d\\\\u0430\\\\u044e, \\\\u0447\\\\u0442\\\\u043e \\\\u0442\\\\u0435\\\\u043f\\\\u0435\\\\u0440\\\\u044c \\\\u0441 \\\\u043d\\\\u0438\\\\u043c \\\\u0434\\\\u0435\\\\u043b\\\\u0430\\\\u0442\\\\u044c\\\\u2026\", \"generation_info\": {\"finish_reason\": \"stop\"}}]', 'llm_string': \"[('_type', 'giga-chat-model'), ('stop', None)]\"}),\n",
       " Document(page_content='Расскажи мне 2 шутки', metadata={'id': 'doc:cache:54d4d5d60796b5c24e291e4179785fe1:78dd6824ae7d48c08c85aececc43351c', 'return_val': '[{\"text\": \"\\\\u041a\\\\u043e\\\\u043d\\\\u0435\\\\u0447\\\\u043d\\\\u043e! \\\\u0412\\\\u043e\\\\u0442 \\\\u0434\\\\u0432\\\\u0435 \\\\u0445\\\\u043e\\\\u0440\\\\u043e\\\\u0448\\\\u0438\\\\u0435 \\\\u0448\\\\u0443\\\\u0442\\\\u043a\\\\u0438:\\\\n\\\\n1. \\\\u041f\\\\u043e\\\\u0447\\\\u0435\\\\u043c\\\\u0443 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u044b \\\\u043d\\\\u0435 \\\\u0438\\\\u0433\\\\u0440\\\\u0430\\\\u044e\\\\u0442 \\\\u0432 \\\\u043a\\\\u0430\\\\u0440\\\\u0442\\\\u044b? \\\\u041f\\\\u043e\\\\u0442\\\\u043e\\\\u043c\\\\u0443 \\\\u0447\\\\u0442\\\\u043e \\\\u043e\\\\u043d\\\\u0438 \\\\u0432\\\\u0441\\\\u0435\\\\u0433\\\\u0434\\\\u0430 \\\\u0432\\\\u044b\\\\u0438\\\\u0433\\\\u0440\\\\u044b\\\\u0432\\\\u0430\\\\u044e\\\\u0442.\\\\n\\\\n2. \\\\u041a\\\\u0430\\\\u043a \\\\u043d\\\\u0430\\\\u0437\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442\\\\u0441\\\\u044f \\\\u0447\\\\u0435\\\\u043b\\\\u043e\\\\u0432\\\\u0435\\\\u043a, \\\\u043a\\\\u043e\\\\u0442\\\\u043e\\\\u0440\\\\u044b\\\\u0439 \\\\u0432\\\\u0441\\\\u0451 \\\\u0437\\\\u0430\\\\u0431\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442? \\\\u0421\\\\u043a\\\\u043b\\\\u0435\\\\u0440\\\\u043e\\\\u0442\\\\u0438\\\\u043a. \\\\u0410 \\\\u043a\\\\u0430\\\\u043a \\\\u043d\\\\u0430\\\\u0437\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442\\\\u0441\\\\u044f \\\\u0442\\\\u043e\\\\u0442, \\\\u043a\\\\u0442\\\\u043e \\\\u0432\\\\u0441\\\\u0451 \\\\u043f\\\\u043e\\\\u043c\\\\u043d\\\\u0438\\\\u0442? \\\\u0417\\\\u043b\\\\u043e\\\\u043f\\\\u0430\\\\u043c\\\\u044f\\\\u0442\\\\u043d\\\\u044b\\\\u0439.\", \"generation_info\": {\"finish_reason\": \"stop\"}}]', 'llm_string': \"[('_type', 'giga-chat-model'), ('stop', None)]\"})]"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "langchain.llm_cache._cache_dict[\n",
    "    \"cache:54d4d5d60796b5c24e291e4179785fe1\"\n",
    "].similarity_search(query=\"Расскажи мне шутку\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "yEpRExARpzEl",
    "outputId": "f0462780-baf6-4b1d-ca1d-3060c4f41c14"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='Расскажи мне 2 шутки', metadata={'id': 'doc:cache:54d4d5d60796b5c24e291e4179785fe1:78dd6824ae7d48c08c85aececc43351c', 'return_val': '[{\"text\": \"\\\\u041a\\\\u043e\\\\u043d\\\\u0435\\\\u0447\\\\u043d\\\\u043e! \\\\u0412\\\\u043e\\\\u0442 \\\\u0434\\\\u0432\\\\u0435 \\\\u0445\\\\u043e\\\\u0440\\\\u043e\\\\u0448\\\\u0438\\\\u0435 \\\\u0448\\\\u0443\\\\u0442\\\\u043a\\\\u0438:\\\\n\\\\n1. \\\\u041f\\\\u043e\\\\u0447\\\\u0435\\\\u043c\\\\u0443 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u044b \\\\u043d\\\\u0435 \\\\u0438\\\\u0433\\\\u0440\\\\u0430\\\\u044e\\\\u0442 \\\\u0432 \\\\u043a\\\\u0430\\\\u0440\\\\u0442\\\\u044b? \\\\u041f\\\\u043e\\\\u0442\\\\u043e\\\\u043c\\\\u0443 \\\\u0447\\\\u0442\\\\u043e \\\\u043e\\\\u043d\\\\u0438 \\\\u0432\\\\u0441\\\\u0435\\\\u0433\\\\u0434\\\\u0430 \\\\u0432\\\\u044b\\\\u0438\\\\u0433\\\\u0440\\\\u044b\\\\u0432\\\\u0430\\\\u044e\\\\u0442.\\\\n\\\\n2. \\\\u041a\\\\u0430\\\\u043a \\\\u043d\\\\u0430\\\\u0437\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442\\\\u0441\\\\u044f \\\\u0447\\\\u0435\\\\u043b\\\\u043e\\\\u0432\\\\u0435\\\\u043a, \\\\u043a\\\\u043e\\\\u0442\\\\u043e\\\\u0440\\\\u044b\\\\u0439 \\\\u0432\\\\u0441\\\\u0451 \\\\u0437\\\\u0430\\\\u0431\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442? \\\\u0421\\\\u043a\\\\u043b\\\\u0435\\\\u0440\\\\u043e\\\\u0442\\\\u0438\\\\u043a. \\\\u0410 \\\\u043a\\\\u0430\\\\u043a \\\\u043d\\\\u0430\\\\u0437\\\\u044b\\\\u0432\\\\u0430\\\\u0435\\\\u0442\\\\u0441\\\\u044f \\\\u0442\\\\u043e\\\\u0442, \\\\u043a\\\\u0442\\\\u043e \\\\u0432\\\\u0441\\\\u0451 \\\\u043f\\\\u043e\\\\u043c\\\\u043d\\\\u0438\\\\u0442? \\\\u0417\\\\u043b\\\\u043e\\\\u043f\\\\u0430\\\\u043c\\\\u044f\\\\u0442\\\\u043d\\\\u044b\\\\u0439.\", \"generation_info\": {\"finish_reason\": \"stop\"}}]', 'llm_string': \"[('_type', 'giga-chat-model'), ('stop', None)]\"}),\n",
       " Document(page_content='Расскажи мне шутку', metadata={'id': 'doc:cache:54d4d5d60796b5c24e291e4179785fe1:587093b63ad44a2daa6c614ccd23dabe', 'return_val': '[{\"text\": \"\\\\u041d\\\\u0430\\\\u0448\\\\u043b\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0432\\\\u0430\\\\u0441 \\\\u0442\\\\u0430\\\\u043a\\\\u0443\\\\u044e \\\\u0448\\\\u0443\\\\u0442\\\\u043a\\\\u0443: \\\\n\\\\n\\\\u2014 \\\\u0410 \\\\u044f \\\\u0432\\\\u0447\\\\u0435\\\\u0440\\\\u0430 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u0430 \\\\u043a\\\\u0443\\\\u043f\\\\u0438\\\\u043b! \\\\n\\\\u2014 \\\\u041d\\\\u0443 \\\\u0438 \\\\u043a\\\\u0430\\\\u043a? \\\\n\\\\u2014 \\\\u041c\\\\u0430\\\\u043b\\\\u0435\\\\u043d\\\\u044c\\\\u043a\\\\u0438\\\\u0439 \\\\u0442\\\\u0430\\\\u043a\\\\u043e\\\\u0439 \\\\u0441\\\\u043b\\\\u043e\\\\u043d\\\\u0451\\\\u043d\\\\u043e\\\\u043a, \\\\u043c\\\\u0438\\\\u043b\\\\u0435\\\\u043d\\\\u044c\\\\u043a\\\\u0438\\\\u0439. \\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430\\\\u043b \\\\u0435\\\\u0433\\\\u043e \\\\u0413\\\\u043e\\\\u0448\\\\u0435\\\\u0439. \\\\u0422\\\\u043e\\\\u043b\\\\u044c\\\\u043a\\\\u043e \\\\u0432\\\\u043e\\\\u0442 \\\\u043d\\\\u0435 \\\\u0437\\\\u043d\\\\u0430\\\\u044e, \\\\u0447\\\\u0442\\\\u043e \\\\u0442\\\\u0435\\\\u043f\\\\u0435\\\\u0440\\\\u044c \\\\u0441 \\\\u043d\\\\u0438\\\\u043c \\\\u0434\\\\u0435\\\\u043b\\\\u0430\\\\u0442\\\\u044c\\\\u2026\", \"generation_info\": {\"finish_reason\": \"stop\"}}]', 'llm_string': \"[('_type', 'giga-chat-model'), ('stop', None)]\"})]"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "langchain.llm_cache._cache_dict[\n",
    "    \"cache:54d4d5d60796b5c24e291e4179785fe1\"\n",
    "].similarity_search(query=\"Расскажи мне 2 шутки\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VspGA_wSokS4"
   },
   "source": [
    "### Вывод\n",
    "\n",
    "Score threshold ключевой фактор в использовании поиска по схожести с помощью Redis."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PBXKgATeWMVZ"
   },
   "source": [
    "## Семантический кэш с GPTCache"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vwFaSeFWWe9r"
   },
   "source": [
    "### Что такое GPTCache?\n",
    "\n",
    "Open source проект, предназначенный для построения и хранения семантического кэша из LLM запросов.\n",
    "\n",
    "Два способа работы:\n",
    "1. Точное совпадение\n",
    "2. Совпадение по схожести\n",
    "\n",
    "GPTCache предназначен для решения следующих задач:\n",
    "1. Как мы генерируем эмбединги для запросов? (via embedding function)\n",
    "2. Как мы храним кэш? (via cache store of data manager, such as SQLite, MySQL, and PostgreSQL. More NoSQL databases will be added in the future)\n",
    "3. Как мы храним и ищем по эмбедингам? (via vector store of data manager, such as FAISS or vector databases such as Milvus. More vector databases and cloud services will be added in the future.)\n",
    "4. How to determine eviction policy? (LRU or FIFO)\n",
    "5. Как мы понимаем, что запрос хранится в кэше? (via evaluation function)\n",
    "\n",
    "Пожалуйста, посмотрите следующий класс для лучшего понимания как решаются эти задачи\n",
    "\n",
    "```python\n",
    "class Cache:\n",
    "   def init(self,\n",
    "            cache_enable_func=cache_all,\n",
    "            pre_embedding_func=last_content,\n",
    "            embedding_func=string_embedding,\n",
    "            data_manager: DataManager = get_data_manager(),\n",
    "            similarity_evaluation=ExactMatchEvaluation(),\n",
    "            post_process_messages_func=first,\n",
    "            config=Config(),\n",
    "            next_cache=None,\n",
    "            **kwargs\n",
    "            ):\n",
    "       self.has_init = True\n",
    "       self.cache_enable_func = cache_enable_func\n",
    "       self.pre_embedding_func = pre_embedding_func\n",
    "       self.embedding_func = embedding_func\n",
    "       self.data_manager: DataManager = data_manager\n",
    "       self.similarity_evaluation = similarity_evaluation\n",
    "       self.post_process_messages_func = post_process_messages_func\n",
    "       self.data_manager.init(**kwargs)\n",
    "       self.config = config\n",
    "       self.next_cache = next_cache\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Sp9gZNC2WXA7"
   },
   "outputs": [],
   "source": [
    "!pip install gptcache --quiet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Zy5CCHJl2yla"
   },
   "outputs": [],
   "source": [
    "import langchain\n",
    "from langchain.llms import GigaChat\n",
    "\n",
    "llm = GigaChat(credentials=\"<Ваши авторизационные данные>\", verify_ssl_certs=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4SHDJ27DW5ny"
   },
   "source": [
    "### Точное совпадение"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "oS_kGsN1XCfm"
   },
   "outputs": [],
   "source": [
    "import hashlib\n",
    "\n",
    "from gptcache import Cache\n",
    "from gptcache.adapter.api import init_similar_cache\n",
    "from gptcache.manager.factory import manager_factory\n",
    "from gptcache.processor.pre import get_prompt\n",
    "from langchain.cache import GPTCache\n",
    "\n",
    "\n",
    "def get_hashed_name(name):\n",
    "    return hashlib.sha256(name.encode()).hexdigest()\n",
    "\n",
    "\n",
    "def init_gptcache(cache_obj: Cache, llm: str):\n",
    "    hashed_llm = get_hashed_name(llm)\n",
    "    cache_obj.init(\n",
    "        pre_embedding_func=get_prompt,\n",
    "        data_manager=manager_factory(manager=\"map\", data_dir=f\"map_cache_{hashed_llm}\"),\n",
    "    )\n",
    "\n",
    "\n",
    "langchain.llm_cache = GPTCache(init_gptcache)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "KlldiTqFrzrm"
   },
   "outputs": [],
   "source": [
    "question = \"What is cache eviction policy?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 107
    },
    "id": "HNF18bP6XE_c",
    "outputId": "3417babb-daf9-4b10-b4fd-362bf9b9b31b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 69.8 ms, sys: 7.14 ms, total: 76.9 ms\n",
      "Wall time: 4.35 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или кэширование) — это стратегия управления памятью, которая позволяет браузеру использовать один и тот же объект или данные несколько раз при обращении к ним. Это достигается путем хранения данных в кэше, который может быть временно удален из памяти браузера, если он больше не нужен. Когда браузер запрашивает данные, которые были сохранены в кэше, они извлекаются из кэша и возвращаются на клиентскую машину. Кэширование помогает ускорить загрузку страницы, так как не требуется каждый раз загружать данные с сервера. Однако, если кэш заполнен или данные, хранящиеся в нем, устарели, то браузер может удалить их из кэша, чтобы освободить память на сервере.'"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(question)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 107
    },
    "id": "nnXOyMwWrtla",
    "outputId": "0f6bb30c-d29d-4177-a02c-cb0387f58900"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 848 µs, sys: 0 ns, total: 848 µs\n",
      "Wall time: 857 µs\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или кэширование) — это стратегия управления памятью, которая позволяет браузеру использовать один и тот же объект или данные несколько раз при обращении к ним. Это достигается путем хранения данных в кэше, который может быть временно удален из памяти браузера, если он больше не нужен. Когда браузер запрашивает данные, которые были сохранены в кэше, они извлекаются из кэша и возвращаются на клиентскую машину. Кэширование помогает ускорить загрузку страницы, так как не требуется каждый раз загружать данные с сервера. Однако, если кэш заполнен или данные, хранящиеся в нем, устарели, то браузер может удалить их из кэша, чтобы освободить память на сервере.'"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(question)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 125
    },
    "id": "Vqp-2H-sBp64",
    "outputId": "2733a0f4-8b19-4803-8ef9-0a5f3cbffa34"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 34.5 ms, sys: 2.65 ms, total: 37.2 ms\n",
      "Wall time: 3.99 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или просто cache eviction) — это стратегия управления кэшем на сервере. Она заключается в том, чтобы удалять старые данные из кэша при необходимости обновления данных в памяти или других областях хранения. \\n\\nПравильная cache eviction policy должна быть гибкой и учитывать различные сценарии использования приложения. Например, если приложение часто использует один и тот же набор данных, то можно удалить эти данные из кэша для ускорения работы приложения. Однако, если приложение не обновляет данные в течение длительного времени, то может потребоваться удаление старых данных из кэша для предотвращения возможных проблем с памятью или производительностью.\\n\\nВажно также убедиться, что приложение регулярно проверяет состояние кэша и удаляет устаревшие данные, чтобы избежать утечек памяти и других проблем производительности.'"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"What is cache eviction   policy?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BixAYE1ysCda"
   },
   "source": [
    "### Поиск по схожести"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "847yNhrlsCEt"
   },
   "outputs": [],
   "source": [
    "import hashlib\n",
    "\n",
    "from gptcache import Cache\n",
    "from gptcache.adapter.api import init_similar_cache\n",
    "from langchain.cache import GPTCache\n",
    "\n",
    "\n",
    "def get_hashed_name(name):\n",
    "    return hashlib.sha256(name.encode()).hexdigest()\n",
    "\n",
    "\n",
    "def init_gptcache(cache_obj: Cache, llm: str):\n",
    "    hashed_llm = get_hashed_name(llm)\n",
    "    init_similar_cache(cache_obj=cache_obj, data_dir=f\"similar_cache_{hashed_llm}\")\n",
    "\n",
    "\n",
    "langchain.llm_cache = GPTCache(init_gptcache)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 143
    },
    "id": "MssW4YLXsL4B",
    "outputId": "ec65150e-2596-4d17-f73e-019496d3c464"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.41 s, sys: 0 ns, total: 1.41 s\n",
      "Wall time: 3.28 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или \"caching policy\") — это стратегия кэширования, которая определяет, какие данные будут храниться в кэше и когда они должны быть удалены из памяти. \\n\\nВ общем случае, cache eviction policy состоит из нескольких шагов:\\n\\n1. Определение размера кэша: размер кэша определяется на основе требований приложения и доступных ресурсов. Обычно он составляет от нескольких килобайт до нескольких мегабайт.\\n\\n2. Выбор алгоритма кэширования: существует множество алгоритмов кэширования, таких как LRU (Least Recently Used), Hash Code, Double Counting, RAM-кэш и другие. Каждый алгоритм имеет свои преимущества и недостатки, поэтому выбор конкретного зависит от требований приложения.\\n\\n3. Установка времени жизни кэша: время жизни кэша может быть установлено на определенный период времени или навсегда. Это позволяет системе периодически очищать кэш для ускорения работы приложений.\\n\\n4. Удаление данных из кэша: если данные больше не нужны приложению, то их можно удалить из кэша. Для этого используется механизм удаления данных из кэша, который может быть реализован с помощью специальных библиотек или API.\\n\\n5. Обновление кэша: после удаления данных из кэша необходимо обновить его содержимое новыми данными. Это делается путем загрузки новых данных из внешнего источника или с диска.\\n\\nВажно отметить, что использование caching policy не гарантирует полной скорости работы приложения, так как некоторые данные могут быть слишком объемными или занимать много места в памяти. Поэтому важно балансировать между использованием кэша и другими методами оптимизации производительности приложения.'"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(question)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 143
    },
    "id": "t0sIlm0xsYf2",
    "outputId": "406fc7af-026e-4007-b63f-ec44f05eb01e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 903 ms, sys: 0 ns, total: 903 ms\n",
      "Wall time: 915 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или \"caching policy\") — это стратегия кэширования, которая определяет, какие данные будут храниться в кэше и когда они должны быть удалены из памяти. \\n\\nВ общем случае, cache eviction policy состоит из нескольких шагов:\\n\\n1. Определение размера кэша: размер кэша определяется на основе требований приложения и доступных ресурсов. Обычно он составляет от нескольких килобайт до нескольких мегабайт.\\n\\n2. Выбор алгоритма кэширования: существует множество алгоритмов кэширования, таких как LRU (Least Recently Used), Hash Code, Double Counting, RAM-кэш и другие. Каждый алгоритм имеет свои преимущества и недостатки, поэтому выбор конкретного зависит от требований приложения.\\n\\n3. Установка времени жизни кэша: время жизни кэша может быть установлено на определенный период времени или навсегда. Это позволяет системе периодически очищать кэш для ускорения работы приложений.\\n\\n4. Удаление данных из кэша: если данные больше не нужны приложению, то их можно удалить из кэша. Для этого используется механизм удаления данных из кэша, который может быть реализован с помощью специальных библиотек или API.\\n\\n5. Обновление кэша: после удаления данных из кэша необходимо обновить его содержимое новыми данными. Это делается путем загрузки новых данных из внешнего источника или с диска.\\n\\nВажно отметить, что использование caching policy не гарантирует полной скорости работы приложения, так как некоторые данные могут быть слишком объемными или занимать много места в памяти. Поэтому важно балансировать между использованием кэша и другими методами оптимизации производительности приложения.'"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(question)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 143
    },
    "id": "Wnq0CDSBsd2y",
    "outputId": "6079e10a-9f9f-4df6-b78c-5e49c90f0025"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 929 ms, sys: 0 ns, total: 929 ms\n",
      "Wall time: 940 ms\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "'Cache eviction policy (или \"caching policy\") — это стратегия кэширования, которая определяет, какие данные будут храниться в кэше и когда они должны быть удалены из памяти. \\n\\nВ общем случае, cache eviction policy состоит из нескольких шагов:\\n\\n1. Определение размера кэша: размер кэша определяется на основе требований приложения и доступных ресурсов. Обычно он составляет от нескольких килобайт до нескольких мегабайт.\\n\\n2. Выбор алгоритма кэширования: существует множество алгоритмов кэширования, таких как LRU (Least Recently Used), Hash Code, Double Counting, RAM-кэш и другие. Каждый алгоритм имеет свои преимущества и недостатки, поэтому выбор конкретного зависит от требований приложения.\\n\\n3. Установка времени жизни кэша: время жизни кэша может быть установлено на определенный период времени или навсегда. Это позволяет системе периодически очищать кэш для ускорения работы приложений.\\n\\n4. Удаление данных из кэша: если данные больше не нужны приложению, то их можно удалить из кэша. Для этого используется механизм удаления данных из кэша, который может быть реализован с помощью специальных библиотек или API.\\n\\n5. Обновление кэша: после удаления данных из кэша необходимо обновить его содержимое новыми данными. Это делается путем загрузки новых данных из внешнего источника или с диска.\\n\\nВажно отметить, что использование caching policy не гарантирует полной скорости работы приложения, так как некоторые данные могут быть слишком объемными или занимать много места в памяти. Поэтому важно балансировать между использованием кэша и другими методами оптимизации производительности приложения.'"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"What is cache eviction   policy?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "1MYGLb8GCiEB",
    "outputId": "2f53b386-b033-4054-9b21-0683164a0d8b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 2.76 s, sys: 0 ns, total: 2.76 s\n",
      "Wall time: 5.48 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "\"I'm sorry, I don't have any peaches in my system right now. Can you please try asking for something else?\""
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Give me a peach\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "id": "e_PX54xiCi7g",
    "outputId": "5dcf087c-cc95-4233-8e17-3594675fadee"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 1.24 s, sys: 0 ns, total: 1.24 s\n",
      "Wall time: 1.53 s\n"
     ]
    },
    {
     "data": {
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      },
      "text/plain": [
       "\"I'm sorry, I don't have any peaches in my system right now. Can you please try asking for something else?\""
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "llm(\"Give me 2 peaches\")"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}